/*@@

	Copyright (c) 2010 BlueSky LTD. All rights reserved. 

	Created_datetime : 	2010-6-22

	File Name :	base_xpackfile.h

	Author : liusiliang (siliangliu@msn.com)

	Description : 
	包文件读取
	XPackFile支持多线程的同时访问。
	XPackFile外部通过XPackElemFileRef来描述其获得的对包内子文件的引用。
	外部勿自行随意改动此描述结构内的内容，以获得正常的包访问功能。

@@*/

#ifndef __BASE_XPACKFILE_H__
#define	__BASE_XPACKFILE_H__

//////////////////////////////////////////////////////////////////////////
//						Header Include
//////////////////////////////////////////////////////////////////////////

#include "utilties.h"
#include "base/base_packfile.h"

#ifdef WIN32
#include <Windows.h>
#else
void* _aligned_malloc(uint sz, uint a);
#endif // WIN32

namespace BLK
{

	//////////////////////////////////////////////////////////////////////////
	//		Global variables and Macro and Structure Definitions
	//////////////////////////////////////////////////////////////////////////

	#define	MAX_XPACKFILE_CACHE			10	// 最大缓存数量

	#define	XPACKFILE_SIGNATURE_FLAG	0x4b415053	//'SPAK'

	#define EXTEND_COMPRESS_SIZE		256

	#define MEM_ALIGNED_ALLOC_SIZE_16	16

	//一个Pack文件具有的头结构
	struct PackFileHeader
	{
		unsigned char	cSignature[4];		//四个字节的文件的头标志，固定为字符串'PACK'
		unsigned long	uCount;				//数据的条目数
		unsigned long	uIndexTableOffset;	//索引的偏移量
		unsigned long	uDataOffset;		//数据的偏移量
		unsigned long	uCrc32;				//校验和
		unsigned char	cReserved[12];		//保留的字节

	};

	//Pack中对应每个子文件的索引信息项
	struct PackIndexInfo
	{
		unsigned long	uId;				//子文件id
		unsigned long	uOffset;			//子文件在包中的偏移位置
		long			lSize;				//子文件的原始大小
		long			lCompressSizeFlag;	//子文件压缩后的大小和压缩方法
											//最高字节表示压缩方法，见XPackMethod
											//低的三个字节表示子文件压缩后的大小
		char			szName[MAXPATH_];	//文件相对路径名称，用来解包
	};

	//////////////////////////////////////////////////////////////////////////
	//						Class Declare
	//////////////////////////////////////////////////////////////////////////

	class XPackFile
	{
	public:
		
		/*
		 *	功能：构造函数
		 */
		XPackFile(void);
		
		/*
		 *	功能：析构函数
		 */
		~XPackFile(void);

		/*
		 *	功能：初始化包文件模块
		 *	返回：是否初始化成功
		 */
		static bool	Initialize(void);

		/*
		 *	功能：关闭包文件模块
		 */
		static void	Terminate(void);

		/*
		 *	功能：打开包文件
		 *	参数：	pszPackFileName --> 打开文件的路径
		 *			nSelfIndex --> 	包自己的索引
		 *	返回：是否正确打开
		 */
		bool Open(
			const char* pszPackFileName, 
			int nSelfIndex);

		/*
		 *	关闭包文件
		 */
		void Close(void);

		/*
		 *	功能：查找包内的子文件
		 *	参数：	uId --> 子文件的id
		 *			ElemRef -->如果找到则在此结构里填上子文件的相关信息
		 *	返回：是否找到
		 */
		bool FindElemFile(
			unsigned long uId, 
			PackElemFileRef& ElemRef);

		/*
		 *	功能：读取子文件一定长度的数据到缓冲区
		 *	参数：	ElemRef --> 用来读取数据的参数
		 *			pBuffer --> 用来读取数据的缓冲区
		 *			uSize --> 要读取的数据的长度
		 *	返回：成功读取得字节数
		 */
		int	ElemFileRead(
			PackElemFileRef& ElemRef, 
			void* pBuffer, 
			unsigned uSize);

		/*
		 *	功能:	得到包文件名
		 *	返回:	包文件名
		 */
		std::string GetFileName(void)
		{
			return m_strFileName;
		}
		
	private:

		/*
		 *	功能：直接读取包文件数据中的数据到缓冲区
		 *	参数：	pBuffer --> 用来读取数据的缓冲区
		 *			uOffset --> 文件在包中的偏移
		 *			uSize --> 需要读取的大小
		 *	返回：成功与否
		 */
		bool DirectRead(
			void* pBuffer, 
			unsigned uOffset, 
			unsigned uSize);

		/*
		 *	功能：带解压地读取包文件到缓冲区
		 *	参数：	pBuffer --> 缓冲区指针
		 *			uExtractSize  --> 数据（期望）解压后的大小，pBuffer缓冲区的大小不小于此数
		 *			lCompressType --> 直接从包中度取得原始（/压缩）大小
		 *			uOffset  --> 从包中的此偏移位置开始读取
		 *			uSize    --> 从包中直接读取得（压缩）数据的大小
		 *	返回：成功与否
		 */
		bool ExtractRead(
			void* pBuffer,
			unsigned uExtractSize,
			long lCompressType,
			unsigned uOffset,
			unsigned uSize);

		/*
		 *	功能：在索引表中查找子文件项(二分法找)
		 *	参数：	ulId --> 需要查找的文件名ID
		 *	返回：如找到返回在索引表中的位置(>=0)，如未找到返回-1
		 */
		int	FindElemFile(unsigned long ulId) const;

		/*
		 *	功能：在cache里查找子文件
		 *	参数：	uId --> 子文件id
		 *			nDesireIndex --> 在cache中的可能位置
		 *	返回：成功则返回cache节点索引(>=0),失败则返回-1
		 */
		int	FindElemFileInCache(
			unsigned uId, 
			int nDesireIndex);

		/*
		 *	功能：把子文件数据添加到cache
		 *	参数：	pBuffer --> 存有子文件数据的缓冲区
		 *			nElemIndex --> 子文件在包中的索引
		 *	返回：添加到cache的索引位置
		 */
		int	AddElemFileToCache(
			void* pBuffer, 
			int nElemIndex);

		/*
		 *	功能：分配缓冲区，并读包内的子文件的内容到其中
		 *	参数：子文件在包内的索引
		 *	返回：成功则返回缓冲区的指针，否则返回空指针
		 */
		void* ReadElemFile(int nElemIndex);

		/*
		 *	功能：释放一个cache结点的数据
		 *	参数：节点在cache中的索引
		 */
		static void	FreeElemCache(int nCacheIndex);

	private:

		std::ifstream			m_File;				//包文件
		unsigned				m_uFileSize;		//包文件大小
		int						m_nElemFileCount;	//子文件的个数
		int						m_nSelfIndex;		//包文件自己在包序列中的索引
		struct PackIndexInfo*	m_pIndexList;		//子文件索引列表

		std::string					m_strFileName;		//文件的名字

		// 子文件数据cache
		struct XPackElemFileCache
		{
			void*			pBuffer;			//保存子文件数据的缓冲区
			unsigned long	uId;				//子文件id
			long			lSize;				//子文件大小
			int				nPackIndex;			//来自哪个包文件
			int				nElemIndex;			//子文件在索引列表中的位置
			unsigned		uRefFlag;			//近期引用标记
		};

		static bool					ms_bInitialize;		//是否已经初始化过了
		//子文件的cache数据
		static	XPackElemFileCache	ms_ElemFileCache[MAX_XPACKFILE_CACHE];
		//子文件被cache的数目
		static	int					ms_nNumElemFileCache;
#ifdef WIN32
		static CRITICAL_SECTION		ms_ReadCritical;	//操作包文件时的临界区控制
#endif
	};

};

#endif
